package wicket.example.components.filter.entries.collapsiblelist.multiselect.component;

import java.util.Iterator;

import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.TransparentWebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.RefreshingView;
import org.apache.wicket.markup.repeater.ReuseIfModelsEqualStrategy;
import org.apache.wicket.model.IModel;

import wicket.example.components.filter.base.AbstractFilterEntry;
import wicket.example.utils.ISelectable;

/**
 * 
 * @author ngolubev
 * 
 *         Base component for collapsible list filter.
 * 
 * @param <T>
 *           - type of model object
 */
public abstract class CollapsibleListFilter<T> extends AbstractFilterEntry
{

   private boolean       opened = Boolean.FALSE;
   private final boolean multiselect;
   private final String  filterHeaderText;

   /**
    * 
    * @param id
    *           - wicket component id.
    * @param multiselect
    *           -
    */
   public CollapsibleListFilter(final String id, final String filterHeaderText, final boolean multiselect)
   {
      super(id);
      this.filterHeaderText = filterHeaderText;
      this.multiselect = multiselect;
   }

   @Override
   protected void onInitialize()
   {
      super.onInitialize();
      final TransparentWebMarkupContainer contentListContainer = new TransparentWebMarkupContainer("contentListContainer")
      {
         @Override
         protected void onConfigure()
         {
            super.onConfigure();
            setVisible(opened);
         }
      };
      contentListContainer.setOutputMarkupId(true);
      contentListContainer.setVisibilityAllowed(true);
      contentListContainer.setOutputMarkupPlaceholderTag(true);
      CollapsibleListFilter.this.add(contentListContainer);
      final TransparentWebMarkupContainer showHideButtonImage = new TransparentWebMarkupContainer("showHideButtonImage")
      {
         @Override
         protected void onComponentTag(ComponentTag tag)
         {
            super.onComponentTag(tag);
            String attributeValue = opened ? "" : "";
            tag.addBehavior(new AttributeModifier("class", attributeValue));
         }
      };
      showHideButtonImage.setOutputMarkupId(true);
      final AjaxLink<?> showHideButton = new AjaxLink<Void>("showHideButton")
      {

         private static final long serialVersionUID = 1L;

         @Override
         public void onClick(AjaxRequestTarget target)
         {
            opened = !opened;
            target.add(showHideButtonImage, contentListContainer);
         }

      };

      showHideButton.add(showHideButtonImage);
      CollapsibleListFilter.this.add(showHideButton);
      final Label filterHeaderValue = new Label("filterHeaderValue", this.filterHeaderText);
      showHideButton.add(filterHeaderValue);
      addContentList(contentListContainer);
   }

   /**
    * 
    * Add AjaxLink for each filter entry.
    * 
    * @param item
    *           - current RefreshingView item.
    * @param contentListContainer
    *           - container for RefreshingView.
    * @param contentList
    *           - RefreshingView for filter entries.
    */
   private void addFilterValueButton(final Item<ISelectable<T>> item, final TransparentWebMarkupContainer contentListContainer,
         final RefreshingView<ISelectable<T>> contentList)
   {
      final AjaxLink<ISelectable<T>> filterValueButton = new AjaxLink<ISelectable<T>>("filterValueButton", item.getModel())
      {

         @Override
         public void onClick(AjaxRequestTarget target)
         {
            if (this.getModelObject().isSelected() && !multiselect) return;
            processSelection(this.getModelObject(), contentList);
            target.add(contentListContainer);
            CollapsibleListFilter.this.onFilterValueClick(target, this.getModelObject());
         }

         @Override
         protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
         {
            super.updateAjaxAttributes(attributes);
            CollapsibleListFilter.this.onClickUpdateAjaxAttributes(attributes);
         }

      };
      final String textValue = CollapsibleListFilter.this.getChoiceValue(item.getModelObject());
      filterValueButton.add(new Label("filterValueButtonText", textValue));
      item.add(filterValueButton);
   }

   /**
    * 
    * Add RefreshingView with filter entries.
    * 
    * @param contentListContainer
    *           - container for RefreshingView.
    */
   private void addContentList(final TransparentWebMarkupContainer contentListContainer)
   {
      final RefreshingView<ISelectable<T>> contentList = new RefreshingView<ISelectable<T>>("contentList")
      {

         @Override
         protected Iterator<IModel<ISelectable<T>>> getItemModels()
         {
            return CollapsibleListFilter.this.getItemModels();
         }

         @Override
         protected void populateItem(Item<ISelectable<T>> item)
         {
            addFilterValueButton(item, contentListContainer, this);
         }

         @Override
         protected Item<ISelectable<T>> newItem(final String id, final int index, final IModel<ISelectable<T>> model)
         {
            return new Item<ISelectable<T>>(id, index, model)
            {
               @Override
               protected void onComponentTag(ComponentTag tag)
               {
                  super.onComponentTag(tag);
                  final boolean selected = this.getModelObject().isSelected();
                  String attributeValue = selected ? "background-color: rgb(232, 232, 232);" : StringUtils.EMPTY;
                  tag.addBehavior(new AttributeModifier("style", attributeValue));
               }
            };
         }

      };
      contentList.setItemReuseStrategy(ReuseIfModelsEqualStrategy.getInstance());
      contentListContainer.add(contentList);
   }

   /**
    * 
    * Change selection state of current selected model object(e.g filter
    * entry). This method is executed after filter entry have been clicked.
    * 
    * @param object
    *           - current selected filter entry.
    * @param contentList
    *           - RefreshingView component.
    * 
    */
   protected void processSelection(final ISelectable<T> object, final RefreshingView<ISelectable<T>> contentList)
   {
      if (!this.multiselect)
      {
         Iterator<Item<ISelectable<T>>> iterator = contentList.getItems();
         while (iterator.hasNext())
         {
            Item<ISelectable<T>> item = iterator.next();
            ISelectable<T> itemObject = item.getModelObject();
            itemObject.setSelected(Boolean.FALSE);
         }
      }
      object.setSelected(!object.isSelected());
   }

   /**
    * Returns an iterator over models for items that will be added to this
    * filter.
    * 
    * @return an iterator over models for items that will be added to this
    *         filter.
    */
   public abstract Iterator<IModel<ISelectable<T>>> getItemModels();

   /**
    * 
    * @param object
    *           - current filter entry model object.
    * @return String which represents text for filter entry Label.
    */
   public abstract String getChoiceValue(ISelectable<T> object);

   /**
    * Calls after filter entry component is clicked.
    * 
    * @param target
    *           - wicket AjaxRequestTarget.
    * 
    * @param object
    *           - current filter entry model object.
    */
   public abstract void onFilterValueClick(AjaxRequestTarget target, ISelectable<T> object);

   /**
    * 
    * @param attributes
    *           - wicket attributes of an Ajax Request.
    */
   public abstract void onClickUpdateAjaxAttributes(AjaxRequestAttributes attributes);

}
